package com.yonyou.pmclouds.workflow.service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.entity.SuperVO;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.BeanHelper;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.organization.entity.OrgConst;
import com.yonyou.pmclouds.organization.entity.OrganizationVO;
import com.yonyou.pmclouds.organization.rmiitf.OrganizationQuery;
import com.yonyou.pmclouds.project.entity.ProjectVO;
import com.yonyou.pmclouds.project.rmiitf.ProjectSimpleQuery;
import com.yonyou.pmclouds.team.entity.TeamMemberVO;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberQuery;
import com.yonyou.pmclouds.workflow.entity.ProcessDefinitionVO;
import com.yonyou.pmclouds.workflow.service.rmiitf.IWorkFlowService;
import com.yonyou.pmclouds.workflow.service.rmiitf.ProcessDefinitionQueryService;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import yonyou.bpm.rest.*;
import yonyou.bpm.rest.param.BaseParam;
import yonyou.bpm.rest.request.AssignInfo;
import yonyou.bpm.rest.request.AssignInfoItem;
import yonyou.bpm.rest.request.Participant;
import yonyou.bpm.rest.request.RestVariable;

import java.io.IOException;
import java.util.*;

@Service
public class ProcessClientService {

    @Value("${bpmrest.server}")
    private String serverUrl;
    @Value("${bpmrest.tenant}")
    private String tenant;
    @Value("${bpmrest.token}")
    private String token;
    @Value("${bpmrest.callbackurl}")
    private String callbackurl;
    @Value("${bpmrest.designerUrl}")
    private String designerUrl;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TeamMemberQuery teamMemberQuery;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IWorkFlowService workFlowService;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProcessDefinitionQueryService definitionQueryService;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectSimpleQuery projectSimpleQuery;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private OrganizationQuery organizationQuery;

    private static final String FIELD_ASSIGN_INFO = "assignInfo";

    private static final String FIELD_ASSIGN_SINGLE = "assignSingle";

    private static final String FIELD_ASSIGN_ALL = "assignAll";

    private static final String FIELD_ASSIGN_ABLE = "assignAble";

    public static ObjectMapper mapper = new ObjectMapper();

    static {
        // 转换为格式化的json
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        // 如果json中有新增的字段并且是实体类类中不存在的，不报错
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public BpmRest bpmRestServices(String userId, String tenantId) throws BusinessException{
        if (StringUtils.isEmpty(userId)) {
            throw new BusinessException("获取BpmRest时传入的userId是空");
        }
        BaseParam baseParam = new BaseParam();
        baseParam.setOperatorID(userId);
        // U审rest服务地址：http://ys.yyuap.com/ubpm-web-rest
        baseParam.setServer(serverUrl);
        // 租户code
        baseParam.setTenant(tenant);
        // 管理端租户管理节点生成的token
        baseParam.setClientToken(token);

        baseParam.setSource(ApplicationConsts.APPLICATION_ID);
        // 租户隔离
        if (!StringUtils.isEmpty(tenantId)) {
            baseParam.setTenantLimitId(tenantId);
        }
        return BpmRests.getBpmRest(baseParam);

    }

    public FormService getDefaultFormService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getFormService();
    }

    public RepositoryService getDefaultRepositoryService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getRepositoryService();
    }

    public IdentityService getDefaultIdentityService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getIdentityService();
    }

    public RuntimeService getDefaultRuntimeService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getRuntimeService();
    }

    public TaskService getDefaultTaskService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getTaskService();
    }

    public HistoryService getDefaultHistoryService(String userId, String tenantId) throws BusinessException{
        return this.bpmRestServices(userId, tenantId).getHistoryService();
    }

    /**
     * 指派检查，assignAble为true，则可指派
     * @return 指派检查结果
     */
    public Map<String, Object> getAssignInfo(JsonNode jsonNode,ProcessDefinitionVO processDefinitionVO, SuperVO businessVO) throws BusinessException{
        try {
            Map<String, Object> assignResultMap = new HashMap<>();
            boolean assignAble = jsonNode.findValue(FIELD_ASSIGN_ABLE).booleanValue();
            assignResultMap.put(FIELD_ASSIGN_ABLE, assignAble);
            if(assignAble) {
                assignResultMap.put(FIELD_ASSIGN_ALL, jsonNode.findValue(FIELD_ASSIGN_ALL).booleanValue());
                assignResultMap.put(FIELD_ASSIGN_SINGLE, jsonNode.findValue(FIELD_ASSIGN_SINGLE).booleanValue());
                AssignInfo assignInfo = mapper.readValue(jsonNode.findValue(FIELD_ASSIGN_INFO).toString(),
                        new TypeReference<AssignInfo>() {});

                // 按项目团队进行过滤，具体需求待定
//                this.assignInfoFilter(assignInfo, processDefinitionVO, businessVO);

                assignResultMap.put(FIELD_ASSIGN_INFO, assignInfo);
            }
            return assignResultMap;
        }catch (IOException e){
            throw new BusinessException("指派检查失败 : " + e.getMessage());
        }
    }

    /**
     * 指派信息按照项目团队过滤
     */
    private void assignInfoFilter(AssignInfo assignInfo, ProcessDefinitionVO definitionVO, SuperVO businessVO) throws BusinessException{
        // 默认为过滤
        if(definitionVO.getIsAssignFilter() != 1) {
            String pkProject = (String) (BeanHelper.getProperty(businessVO, CommonFieldConst.PKPROJECT) != null ?
                    BeanHelper.getProperty(businessVO, CommonFieldConst.PKPROJECT) :
                    BeanHelper.getProperty(businessVO, CommonFieldConst.PK_PROJECT));
            if(StringUtils.isNotEmpty(pkProject)) {
                TeamMemberVO[] teamMemberVOS = teamMemberQuery.queryByProject(pkProject);
                // 项目团队和指派人员取交集
                if(teamMemberVOS == null) {
                    throw new BusinessException("项目团队为空，请维护成员之后再提交");
                }
                AssignInfoItem[] assignInfoItems = assignInfo.getAssignInfoItems();
                if(assignInfoItems != null && assignInfoItems.length > 0) {
                    Participant[] participants = assignInfoItems[0].getParticipants();
                    List<Participant> newParticipants = new ArrayList<>();
                    if(participants != null && participants.length > 0) {
                        for(TeamMemberVO memberVO : teamMemberVOS) {
                            for(Participant participant : participants) {
                                if(memberVO.getPkUser().equalsIgnoreCase(participant.getId())) {
                                    newParticipants.add(participant);
                                    break;
                                }
                            }
                        }
                        if(newParticipants.size() <= 0) {
                            throw new BusinessException("项目团队中未找到环节点[" + assignInfoItems[0].getActivityName()
                                    + "]配置的指派人员，请维护成员之后再提交");
                        }
                        assignInfoItems[0].setParticipants(newParticipants.toArray(new Participant[]{}));
                    }
                }
            }

        }
    }

    public List<RestVariable> getVariableList(SuperVO businessVO) throws BusinessException{
        return workFlowService.getVariableList(businessVO);
    }

    public ProcessDefinitionVO getDefinitionByType(String billType, SuperVO businessVO) throws BusinessException{
        //通过确定项目主键的属性名，来获得这张单据里的项目主键
        String pkProject = (String) (BeanHelper.getProperty(businessVO, CommonFieldConst.PKPROJECT) != null ?
                BeanHelper.getProperty(businessVO, CommonFieldConst.PKPROJECT) :
                BeanHelper.getProperty(businessVO, CommonFieldConst.PK_PROJECT));
        //根据项目主键查询出项目对象
        ProjectVO projectVO = projectSimpleQuery.queryProject(pkProject);
        String pkOrg = null;
        if(projectVO != null) {
            String pkSrc = projectVO.getPkOrg();
            //判断NC主键是否为空，并且也不为“~”，确定该项目为NC项目则进入if语句
            if(StringUtils.isNotEmpty(pkSrc) && !"~".equalsIgnoreCase(pkSrc)) {
                OrganizationVO organizationVO = organizationQuery.queryByPkSrc(RuntimeEnvironment.getTenantId(), pkSrc);
                // 如果未找到启动的组织，则默认查找租户下的流程
                if(organizationVO != null && Objects.equals(organizationVO.getEnableState(), OrgConst.ENABLE_STATE)) {
                    pkOrg = organizationVO.getPkOrganization();
                }
            }
        }

        if (StringUtils.isEmpty(billType))
            throw new BusinessException("请指明单据类型");
        //根据租户主键、单据类别以及组织主键查询出流程对象
        ProcessDefinitionVO[] processDefinitionVOs = definitionQueryService.queryProcDefVOByBilltype(billType,
                RuntimeEnvironment.getTenantId(), pkOrg);
        if (ArrayUtils.isEmpty(processDefinitionVOs)) {
            // 如果存在组织但是未找到组织配置的启用的审批流，则查找集团审批流。就代表pkOrg的值就是pkTenant的值
            if(pkOrg != null) {
                processDefinitionVOs = definitionQueryService.queryProcDefVOByBilltype(billType,
                        RuntimeEnvironment.getTenantId(), null);
                if (ArrayUtils.isEmpty(processDefinitionVOs)) {
                    return null;
                }
            }else{
                return null;
            }
        }
        //之所以返回下标为0的值，是因为它在SQL语句中就已经写死了只查询状态为启用状态的审批流，所以只有一条数据。
        return processDefinitionVOs[0];
    }

}
